// 设置蛇的类
class Snake {
  // 蛇所在的元素
  element: HTMLElement
  // 蛇头
  head: HTMLElement
  // 蛇的身体(包含头)
  bodies: HTMLCollection

  constructor() {
    this.element = document.getElementById('snake')!
    // 类型断言
    this.head = document.querySelector('#snake>div') as HTMLElement
    this.bodies = this.element.getElementsByTagName('div')
  }

  // 获取蛇头x坐标
  get X() {
    return this.head.offsetLeft
  }
  // 获取蛇头y坐标
  get Y() {
    return this.head.offsetTop
  }

  // 设置蛇头坐标
  set X(value: number) {
    // 因为只移动一个方向 新旧只一样不修改
    if (this.X === value) return
    // X合法范围0-290
    if (value < 0 || value > 290) {
      throw new Error('蛇撞墙了')
    }

    // 修改x时 是在修改水平方向的坐标，蛇在左右移动，设置在移动时不能向右掉头，反之亦然
    // 注意：注意开第二个
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
      // 如果发生掉头，让蛇反方向继续移动 即和原理移动方向一致
      if (value > this.X) {
        // 如果新值大于旧值 则说明蛇在向右走，此时发生掉头，应该使蛇继续向左
        value = this.X - 10 // 手动修改移动方向
      } else {
        value = this.X + 10
      }
    }


    // 移动蛇身体
    this.moveBody()

    this.head.style.left = value + 'px'

    // 判断是否撞到自己
    this.checkHeadBody()
  }

  // 设置蛇头坐标
  set Y(value: number) {
    // 因为只移动一个方向 新旧只一样不修改
    if (this.Y === value) return
    // Y合法范围0-290
    if (value < 0 || value > 290) {
      throw new Error('蛇撞墙了' + ' Game Over')
    }

    // 修改y时 是在修改垂直方向的坐标，蛇在上下移动，设置在移动时不能向下掉头，反之亦然
    // 注意：注意开第二个
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
      // 如果发生掉头，让蛇反方向继续移动 即和原理移动方向一致
      if (value > this.Y) {
        // 如果新值大于旧值 则说明蛇在向下走，此时发生掉头，应该使蛇继续向上
        value = this.Y - 10
      } else {
        value = this.Y + 10
      }
    }

    // 移动蛇身体
    this.moveBody()

    this.head.style.top = value + 'px'

    // 判断是否撞到自己
    this.checkHeadBody()
  }

  // 蛇增加身体方法
  addBody() {
    let divEle = document.createElement('div')
    // 向element中添加一个div
    // this.element.insertAdjacentElement('beforeend', divEle)
    this.element.appendChild(divEle)
  }

  // 移动蛇身体
  moveBody() {
    /*
    原理：将后面的身体设置为前面身体的位置 一定要从最后开始计算位置
    第4节 = 第3节位置
    第3节 = 第2节位置
    第2节 = 第1节位置
    */
    for (let i = this.bodies.length - 1; i > 0; i--) {
      let X = (this.bodies[i - 1] as HTMLElement).offsetLeft;
      let Y = (this.bodies[i - 1] as HTMLElement).offsetTop;

      // 因为新增的身体加了定位
      // 所有将值设置到当前身体上
      (this.bodies[i] as HTMLElement).style.left = X + 'px';
      (this.bodies[i] as HTMLElement).style.top = Y + 'px';
    }
  }

  // 蛇头是否撞到身体的方法
  checkHeadBody() {
    // 获取所有的身体 检测是否和蛇头的坐标发升重叠
    for (let i = 1; i < this.bodies.length; i++) {
      let bd = this.bodies[i] as HTMLElement
      if (this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
        // 进入说明撞到了自己 游戏结束
        throw new Error("撞到自己了");
      }
    }
  }
}

export default Snake